﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;

using OptusMobile.Web.ServerComponents.BAL.RecommenderManagement;
using OptusMobile.Web.ServerComponents.DAL;
using RecommenderAlgorithmLibrary;
using System.Collections;

namespace AlgorithmTester
{
    public partial class TesterStarterForm : Form
    {
        private int finishedStep = 0;
        string ITEM = "Item";
        string USER = "User";
        private int TOPN = 5;
        private float?[][] itemSimilarities;
        int?[][] originalRatings;
        float?[][] predictedRatings;
        float?[][] userSimilarities;
        float?[] targetUserFinalRatings;
        string COSINE = "Cosine";
        string PEARSON = "Pearson";

        IBCFandUBCFHybridAlgorithm hybridAlgorithm = new IBCFandUBCFHybridAlgorithm();
        DAL dal = new DAL();

        public TesterStarterForm()
        {
            InitializeComponent();
            this.dataGridViewUserSimilarities.AutoGenerateColumns = false;
            LoadData();
        }

        private void LoadData()
        {
            List<UserRatings> ratings = LoadTestDataFromXML();
            FillTestDatatoGridView(ratings);

            //LoadMovielensData();

            this.comboBoxTargetUser.SelectedIndex = 0;
            this.comboBoxAlgorithm.Items.Add(PEARSON);
            this.comboBoxAlgorithm.Items.Add(COSINE);
            this.comboBoxAlgorithm.SelectedIndex = 0;

        }

        private void LoadMovielensData()
        {
             this.dal.ReadTrainingRatings("AllRatings");
        }

        private void FillTestDatatoGridView(List<UserRatings> ratings)
        {
            int usersNumber = ratings.Count;
            int itemsNumber = ratings[0].Ratings.Count;

            this.dataGridViewOriginalRatings.Columns.Add("User", "User");
            for (int i = 1; i <= itemsNumber; i++)
            {
                this.dataGridViewOriginalRatings.Columns.Add(ITEM + i.ToString(), ITEM + i.ToString());
            }

            for (int i = 0; i < usersNumber; i++)
            {
                dataGridViewOriginalRatings.Rows.Add();
            }

            for (int i = 0; i < usersNumber; i++)
            {
                dataGridViewOriginalRatings.Rows[i].Cells[0].Value = USER + (i+1).ToString();
                this.comboBoxTargetUser.Items.Add(USER + (i + 1).ToString());

                for (int j = 1; j <= itemsNumber; j++)
                {
                    dataGridViewOriginalRatings.Rows[i].Cells[j].Value = ratings[i].Ratings[j-1];
                }
            }

            this.textBoxUserNo.Text = usersNumber.ToString();
            this.textBoxItemNumber.Text = itemsNumber.ToString();
        }

        private List<UserRatings> LoadTestDataFromXML()
        {
            string filePath = "TestData.xml";

            XDocument testfile = XDocument.Load(filePath);
            List<UserRatings> ratings = (from userRatings in testfile.Element("Ratings").Elements("User")
                                         select new UserRatings
                                         {
                                             UserID = userRatings.Attribute("id").Value,
                                             Ratings = SplitRatings(userRatings.Attribute("ratings").Value)
                                         }).ToList();
            return ratings;
        }

        private List<int?> SplitRatings(string str)
        {
            string[] strs = str.Split(new char[] {','});
            List<int?> ratings = new List<int?>();
            foreach (string rating in strs)
            {
                if (rating.Trim() == string.Empty)
                {
                    ratings.Add(null);
                }
                else
                {
                    ratings.Add(Convert.ToInt32(rating));
                }
            }
            return ratings;
        }

        private void buttonBegin_Click(object sender, EventArgs e)
        {
            this.originalRatings = ReadDataGridViewtoArray();
            if (this.comboBoxAlgorithm.SelectedIndex == 1)
            {
                this.itemSimilarities = hybridAlgorithm.CalculateItemSimilarityUsingCosine(this.originalRatings);
            }
            else
            {
                this.itemSimilarities = hybridAlgorithm.CalculateItemSimilarityUsingPearsonCorrelation(this.originalRatings);
            }

            FillArrayToDatagridview("Item", "Item", this.itemSimilarities,this.dataGridViewItemSimilarities);
            this.tabControl1.SelectedIndex = 1;

            if (this.finishedStep < 1)
            {
                this.finishedStep = 1;
                this.labelStep.Text = "1";
            }
        }

        private int?[][] ReadDataGridViewtoArray()
        {
            int userNumber = Convert.ToInt32(textBoxUserNo.Text);
            int itemNumber = Convert.ToInt32(textBoxItemNumber.Text);
            int?[][] ratings = new int?[userNumber][];

            for (int i = 0; i < userNumber; i++)
            {
                int?[] rt = new int?[itemNumber];
                for (int j = 1; j <= itemNumber; j++)
                {
                    object cell = dataGridViewOriginalRatings.Rows[i].Cells[j].Value;
                    int? rating = null;
                    if (cell != null)
                    {
                        rating = Convert.ToInt32(cell);
                    }
                    rt[j-1] = rating;
                }
                ratings[i] = rt;
            }

            return ratings;
        }

        private void buttonPredict_Click(object sender, EventArgs e)
        {
            if (this.finishedStep < 1)
            {
                MessageBox.Show("Error, please finish the previous steps first", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                PredictRatingsMatrix();

                FillArrayToDatagridview("Item", "User", this.predictedRatings, this.dataGridViewPredictedRatings);
                this.tabControl1.SelectedIndex = 2;
                if (this.finishedStep < 2)
                {
                    this.finishedStep = 2;
                    this.labelStep.Text = "2";
                }
            }

        }

        private void PredictRatingsMatrix()
        {
            this.predictedRatings = hybridAlgorithm.PredictRatings(this.originalRatings, this.itemSimilarities, this.TOPN);
        }

        private void buttonUserSimilarity_Click(object sender, EventArgs e)
        {
            if (this.finishedStep < 2)
            {
                MessageBox.Show("Error, please finish the previous steps first", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                CalculateUsersSimilarity();

                FillArrayToDatagridview("User", "User", this.userSimilarities, this.dataGridViewUserSimilarities);
                this.tabControl1.SelectedIndex = 3;
                if (this.finishedStep < 3)
                {
                    this.finishedStep = 3;
                    this.labelStep.Text = "3";
                }
            }
        }

        private void CalculateUsersSimilarity()
        {
            this.userSimilarities = hybridAlgorithm.CalculateUserSimilarities(this.predictedRatings);
        }

        private void buttonPredictRatings_Click(object sender, EventArgs e)
        {
            if (this.finishedStep < 1)
            {
                MessageBox.Show("Error, please finish the previous steps first", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                this.targetUserFinalRatings = PredictFinalRatingsforUser();

                float?[][] tempArray = new float?[1][];
                tempArray[0] = this.targetUserFinalRatings;
                FillArrayToDatagridview("Item", "User", tempArray, this.dataGridViewFinalRatings);
                this.dataGridViewFinalRatings.Rows[0].Cells[0].Value = this.comboBoxTargetUser.SelectedItem.ToString();
                this.tabControl1.SelectedIndex = 4;
                if (this.finishedStep < 4)
                {
                    this.finishedStep = 4;
                    this.labelStep.Text = "4";
                }

                //add final results
                int selectedUserIndex = this.comboBoxTargetUser.SelectedIndex;
                List<int> indexes = hybridAlgorithm.RecommendItems(this.originalRatings, selectedUserIndex, 10, 10);
                
                for (int i = 0; i < indexes.Count; i++)
                {
                    this.dataGridViewFinalRatings.Rows[1].Cells[i].Value = indexes[i].ToString();
                }
            }

        }
         
        private float?[] PredictFinalRatingsforUser()
        {
            int selectedUserIndex = this.comboBoxTargetUser.SelectedIndex;
            return hybridAlgorithm.CalculateFinalUserRatings(selectedUserIndex, this.originalRatings, this.userSimilarities[selectedUserIndex], this.predictedRatings, TOPN);
        }

        private void FillArrayToDatagridview(string columnName, string RowName, float?[][] dataMatrix,DataGridView datagridview)
        {
            int usersNumber = dataMatrix.Length;
            int itemsNumber = dataMatrix[0].Length;

            datagridview.Rows.Clear();
            datagridview.Columns.Clear();

            datagridview.Columns.Add("", "      ");
            for (int i = 1; i <= itemsNumber; i++)
            {
                datagridview.Columns.Add(columnName + i.ToString(), columnName + i.ToString());
            }

            for (int i = 0; i < usersNumber; i++)
            {
                datagridview.Rows.Add();
            }

            for (int i = 0; i < usersNumber; i++)
            {
                datagridview.Rows[i].Cells[0].Value = RowName + (i + 1).ToString();
                for (int j = 1; j <= itemsNumber; j++)
                {
                    datagridview.Rows[i].Cells[j].Value = dataMatrix[i][j - 1];
                }
            }

        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.TOPN = Convert.ToInt32(this.textBoxTopN.Text);
        }

        private void movielensAssessmentToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MovielensAssessmentForm form = new MovielensAssessmentForm();
            form.Show();
        }

    }
}
